#include <iostream>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "bglProgram.h"
#include "bglTexture2D.h"
#include "bglWidget.h"

class BaseWidget : public BGLWidget
{
private:
	unsigned int m_VBO; //顶点缓冲区对象
	unsigned int m_VAO; //顶点数组对象
	
	unsigned int m_textureId0;  //纹理
	unsigned int m_textureId1;

	BGLProgram m_program;

	glm::vec3 m_cameraPos   = glm::vec3(0.0f, 0.0f,  3.0f);
	glm::vec3 m_cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
	glm::vec3 m_cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);

	float m_deltaTime = 0.0f; // 当前帧与上一帧的时间差
	float m_lastFrame = 0.0f; // 上一帧的时间


public:
	BaseWidget() : BGLWidget()
	{
		m_cameraPos   = glm::vec3(0.0f, 0.0f,  3.0f);
		m_cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
		m_cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);
	}

	~BaseWidget()
	{

	}

	virtual void Init() 
	{
		//6个面 x 每个面有2个三角形组成 x 每个三角形有3个顶点
		float vertices[] = {
			-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
			 0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
			 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
			 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
			-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
			-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

			-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
			 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
			 0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
			 0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
			-0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
			-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

			-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
			-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
			-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
			-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
			-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
			-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

			0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
			0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
			0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
			0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
			0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
			0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

			-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
			 0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
			 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
			 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
			-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
			-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

			-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
			 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
			 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
			 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
			-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
			-0.5f,  0.5f, -0.5f,  0.0f, 1.0f
		};

		glGenVertexArrays(1, &m_VAO); //创建顶点数组对象
		glBindVertexArray(m_VAO); //绑定顶点数组

		glGenBuffers(1, &m_VBO);  //创建顶点缓冲区
		glBindBuffer(GL_ARRAY_BUFFER, m_VBO); //绑定顶点缓冲区
		glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //GL_STATIC_DRAW:数据不会或几乎不会改变。

		//设置顶点属性的属性
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
		glEnableVertexAttribArray(0); //启用0号顶点属性

		glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
		glEnableVertexAttribArray(1); //启用1号顶点属性

		m_textureId0 = BGLTexture2D().createTextureFromImage("./data/images/girl0.jpg");
		m_textureId1 = BGLTexture2D().createTextureFromImage("./data/images/face.png");

		m_program.createProgramFromFile("./data/shaders/08_3d1/vert.glsl", "./data/shaders/08_3d1/frag.glsl"); //创建shader program
		glEnable(GL_DEPTH_TEST);
		//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //绘制模式
		//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);//
	}

	virtual void Render()
	{
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		int screenWidth, screenHeight;
		GetWindowSize(&screenWidth, &screenHeight);

		glm::vec3 cubePositions[] = {
			glm::vec3( 0.0f,  0.0f,  0.0f), 
			glm::vec3( 2.0f,  5.0f, -15.0f), 
			glm::vec3(-1.5f, -2.2f, -2.5f),  
			glm::vec3(-3.8f, -2.0f, -12.3f),  
			glm::vec3( 2.4f, -0.4f, -3.5f),  
			glm::vec3(-1.7f,  3.0f, -7.5f),  
			glm::vec3( 1.3f, -2.0f, -2.5f),  
			glm::vec3( 1.5f,  2.0f, -2.5f), 
			glm::vec3( 1.5f,  0.2f, -1.5f), 
			glm::vec3(-1.3f,  1.0f, -1.5f)  
		};

		m_program.begin();

		glUniform1i(glGetUniformLocation(m_program.getId(), "ourTexture0"), 0);
		glUniform1i(glGetUniformLocation(m_program.getId(), "ourTexture1"), 1);

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, m_textureId0);

		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, m_textureId1);

		glBindVertexArray(m_VAO);
		for(unsigned int i = 0; i < 10; i++)
		{
			glm::mat4 model = glm::mat4(1.0f);
			model = glm::rotate(model, (float)glfwGetTime() * glm::radians(20.0f), glm::vec3(-0.5f, -1.0f, 0.0f));
			model = glm::translate(model, cubePositions[i]);
			model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f));
			m_program.setMat4("model", model);

			glm::mat4 view = glm::mat4(1.0f);
			view = glm::lookAt(m_cameraPos, m_cameraPos + m_cameraFront, m_cameraUp);
			m_program.setMat4("view", view);

			glm::mat4 projection = glm::mat4(1.0f);
			projection = glm::perspective(glm::radians(45.0f), (float)screenWidth / screenHeight, 0.1f, 100.0f);
			m_program.setMat4("projection", projection);

			glDrawArrays(GL_TRIANGLES, 0, 36);  //总共有36个顶点。
		}
		
		m_program.end();
	}

	virtual void FramebufferSizeCallback(GLFWwindow* window, int width, int height)
	{
		if(width != 0 && height != 0)
		{
			glViewport(0, 0, width, height);
		}
	}

	virtual void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
	{
		if (yoffset > 0)
		{
			m_cameraPos.z *= (float)0.8;
		}
		else
		{
			m_cameraPos.z *= (float)1.2;
		}	
	}

	virtual void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
	{
		float currentFrame = (float)glfwGetTime();
		m_deltaTime = currentFrame - m_lastFrame;
		m_lastFrame = currentFrame;
	
		float cameraSpeed = 2.5f * m_deltaTime; // adjust accordingly
		if (key == GLFW_KEY_W && (action == GLFW_REPEAT || action == GLFW_PRESS))
			m_cameraPos += cameraSpeed * m_cameraFront;
		if (key == GLFW_KEY_S && (action == GLFW_REPEAT || action == GLFW_PRESS))
			m_cameraPos -= cameraSpeed * m_cameraFront;
		if (key == GLFW_KEY_A && (action == GLFW_REPEAT || action == GLFW_PRESS))
			m_cameraPos -= glm::normalize(glm::cross(m_cameraFront, m_cameraUp)) * cameraSpeed;
		if (key == GLFW_KEY_D && (action == GLFW_REPEAT || action == GLFW_PRESS))
			m_cameraPos += glm::normalize(glm::cross(m_cameraFront, m_cameraUp)) * cameraSpeed;
	}
}; 



int main() {
	
	BaseWidget widget;
	widget.Run();

	return 0;
}